﻿using System;
using System.Text;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading;
using System.Windows.Forms;
using System.Xml.Serialization;

namespace CEFileTransferCE
{
    [Serializable]
    public class MySettings
    {
        public MySettings()
        {
            Settings = this;
        }
        public static MySettings Settings;
        public int Port { get; set; }
        //public int IndxIPAddressToUse { get; set; }
        public string DefaultRecvFolder { get; set; }

        public string SendToIPAddress { get; set; }

        static public void SerializeToXML()
        {
            try
            {
                string appName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
                string appPath = System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase;
                appPath = appPath.ToLower().Replace(appName.ToLower() + ".exe", "").Replace("file:///", "").Replace("/", "\\");
                XmlSerializer serializer = new XmlSerializer(typeof(MySettings));
                TextWriter textWriter = new StreamWriter(appPath + "CEFileTransfer.xml");
                serializer.Serialize(textWriter, Settings);
                textWriter.Close();
            }
            catch (Exception ex)
            {
            }
        }

        static public void DeserializeFromXML()
        {
            try
            {
                string appName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
                string appPath = System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase;
                appPath = appPath.ToLower().Replace(appName.ToLower() + ".exe", "").Replace("file:///", "").Replace("/", "\\");
                XmlSerializer deserializer = new XmlSerializer(typeof(MySettings));
                TextReader textReader = new StreamReader(appPath + "CEFileTransfer.xml");
                Settings = null;
                Settings = (MySettings)deserializer.Deserialize(textReader);
                textReader.Close();
            }
            catch (Exception ex)
            {
            }

        }

    }
    class Program
    {

        public static int Port = 5656;
        //public static int indxIPAddressToUse = 2;
        public static string defaultRecvFolder = "\\temp\\";
        public static byte[] serverIPAddress { get; set; }


        //[STAThread]
        static void Main(string[] args)
        {

            MySettings.DeserializeFromXML();
            Port = MySettings.Settings.Port;
            defaultRecvFolder = MySettings.Settings.DefaultRecvFolder;

            if (args.Length == 4)
            {
                if (args[0].ToLower() == "cfg")
                {
                    try
                    {
                        MySettings settings = new MySettings();

                        settings.Port = int.Parse(args[1]);
                        //settings.IndxIPAddressToUse = int.Parse(args[2]);
                        settings.DefaultRecvFolder = args[2];
                       
                        //Go through this so as to generate an error if incorrect.
                        IPAddress ip = IPAddress.Parse(args[3]);
                        settings.SendToIPAddress = ip.ToString();

                        Console.WriteLine("Saving new confoguration.");
                        MySettings.SerializeToXML();

                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        DoInfo();
                    }
                    Console.WriteLine("Press [Enter] to finish.");
                    Console.ReadLine();
                    Application.Exit();
                }
                else
                {
                    DoInfo();
                    Console.ReadLine();
                    Application.Exit();
                }
            }
            else if (args.Length == 0)
            //Default is server
            {
                Server2 svr = new Server2();
                svr.RunServer(defaultRecvFolder, Port);
                Console.WriteLine("Please wait. Press [Enter] when download is complete.");
                Console.ReadLine();
            }
            else if (args.Length == 1)
            {
                if (args[0].ToLower() == "svr")
                {
                    Server2 svr = new Server2();
                    svr.RunServer(defaultRecvFolder, Port);
                    Console.WriteLine("Please wait. Press [Enter] when download is complete.");
                    Console.ReadLine();
                }
                else if (args[0].ToLower() == "diag")
                {

                    OpenFileDialog openFileDialog1 = new OpenFileDialog();

                    openFileDialog1.InitialDirectory = defaultRecvFolder;
                    openFileDialog1.Filter = "All files (*.*)|*.*";
                    openFileDialog1.FilterIndex = 1;
                    //openFileDialog1..RestoreDirectory = false;

                    if (openFileDialog1.ShowDialog() == DialogResult.OK)
                    {
                        string filename = openFileDialog1.FileName;
                        if (File.Exists(filename))
                        {
                            Client(filename, Port);
                            Console.WriteLine("Press [Enter] to finish.");
                            Console.ReadLine();
                        }
                    }

                }
                else if (args[0].Contains("?"))
                {
                    DoInfo();
                    Console.ReadLine();
                }
                else
                    //Only one param as file path
                {
                    Client(args[0], Port);
                    Console.WriteLine("Press [Enter] to finish.");
                    Console.ReadLine();
                }

            }
            else
            {
                if (args[0].ToLower() == "svr")
                {
                    Server2 svr = new Server2();
                    svr.RunServer(args[1], Port);
                    Console.WriteLine("Please wait. Press [Enter] when download is complete.");
                    Console.ReadLine();
                }
                else if (args[0].ToLower() == "cli")
                {
                    Client(args[1], Port);
                    Console.WriteLine("Press [Enter] to finish.");
                    Console.ReadLine();
                }
                else
                {
                    DoInfo();
                    Console.ReadLine();
                }
            }
           

        }

        public static void DoInfo()
        {
            Console.WriteLine("FileTransfer over TCPIP using port {0}.", Port);

            Console.WriteLine("FileTransfer [svr|cli]  [Folder Path|File Path]", Port);
            Console.WriteLine();
            Console.WriteLine("No parameters: Defaults to sever mode.\r\n\tStore received file in: {0}", defaultRecvFolder);
            Console.WriteLine();
            Console.WriteLine("Command line = <File Path>   \r\n\tSend mode.\tSend file: {0}", "<File Path>"); Console.WriteLine();
            Console.WriteLine("Command line = diag: \r\n\tSend Mode.\tOpen file dialog for file to send."); Console.WriteLine();
            Console.WriteLine("Command line = svr:  \r\n\tServer Mode.\tStore received file in: {0}", defaultRecvFolder); Console.WriteLine();
            Console.WriteLine("Command line = svr  <Folder Path> \r\n\tServer mode.\tStore received file in: {0}", "<Folder Path>"); Console.WriteLine();
            Console.WriteLine("Command line = cli  <File Path>   \r\n\tSend mode.\tSend file: {0}", "<File Path>"); Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("To set configurartion:");
            Console.WriteLine("FileTransfer cfg  <Port> <Default Recv Dir> <Sever IP Address>");
            Console.WriteLine("<Server IP Address> is needed for by the client sending a file");
            Console.WriteLine();

            Console.WriteLine("Press [Enter] to finish.");
        }

        /// <summary>
        /// Uses synchronous transfer. Limited to small files (8.4K?)
        /// </summary>
        /// <param name="receivedPath"></param>
        /// <param name="port"></param>
        public static void Server(string receivedPath, int port)
        {
            //Ref http://socketprogramming.blogspot.com.au/2007/11/file-transfer-using-c-socket.html
            try
            {

                IPEndPoint ipEnd = new IPEndPoint(IPAddress.Any, port);

                Socket sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);

                sock.Bind(ipEnd);

                sock.Listen(1000);

                Socket clientSock = sock.Accept();


                byte[] clientData = new byte[1024 * 5000];

                //string receivedPath = "\\";


                int receivedBytesLen = clientSock.Receive(clientData);

                int fileNameLen = BitConverter.ToInt32(clientData, 0);

                string fileName = Encoding.ASCII.GetString(clientData, 4, fileNameLen);

                Console.WriteLine("Client:{0} connected & File {1} started received.", clientSock.RemoteEndPoint, fileName);

                using (BinaryWriter bWrite = new BinaryWriter(File.Open(receivedPath + fileName, FileMode.Create)))
                {
                    bWrite.Write(clientData, 4 + fileNameLen, receivedBytesLen - 4 - fileNameLen);
                }

                FileInfo fi = new FileInfo(receivedPath + fileName);

                Console.WriteLine("File: {0} received. Saved at: {1}. {2} bytes received.", fileName, receivedPath, fi.Length);


                clientSock.Close();


            }

            catch (Exception ex)
            {

                Console.WriteLine("File Receiving fail." + ex.Message);

            }

        }

        /// <summary>
        /// Uses asynchronous transfer. Suitable for large files
        /// </summary>
        public class Server2
        {
            public void RunServer(string ReceivedPath, int port)
            // Ref: http://codetechnic.blogspot.com.au/2009/02/receiving-large-files-over-tcpip-in-c.html
            {
                receivedPath = ReceivedPath;
                flag = 0;
                notDone = true;
                t1 = new Thread(new ThreadStart(StartListening));
                t1.Start();
            }
            System.Threading.Thread t1;
            int flag = 0;
            bool notDone = true;
            string receivedPath = "\\";

            public delegate void MyDelegate();

            public ManualResetEvent allDone = new ManualResetEvent(false);

            public class StateObject
            {
                // Client socket.
                public Socket workSocket = null;

                public const int BufferSize = 1024;
                // Receive buffer.
                public byte[] buffer = new byte[BufferSize];
            }



            public void StartListening()
            {
                byte[] bytes = new Byte[1024];
                IPEndPoint ipEnd = new IPEndPoint(IPAddress.Any, Port);
                Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                try
                {
                    listener.Bind(ipEnd);
                    listener.Listen(100);
                    while (notDone)
                    {
                        allDone.Reset();
                        listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
                        allDone.WaitOne();

                    }
                }
                catch (Exception ex)
                {

                }

            }
            public void AcceptCallback(IAsyncResult ar)
            {

                allDone.Set();


                Socket listener = (Socket)ar.AsyncState;
                Socket handler = listener.EndAccept(ar);


                StateObject state = new StateObject();
                state.workSocket = handler;
                handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                new AsyncCallback(ReadCallback), state);
                flag = 0;
            }

            public void ReadCallback(IAsyncResult ar)
            {

                int fileNameLen = 1;
                String content = String.Empty;
                StateObject state = (StateObject)ar.AsyncState;
                Socket handler = state.workSocket;
                int bytesRead = handler.EndReceive(ar);
                if (bytesRead > 0)
                {

                    if (flag == 0)
                    {
                        fileNameLen = BitConverter.ToInt32(state.buffer, 0);
                        string fileName = Encoding.UTF8.GetString(state.buffer, 4, fileNameLen);
                        receivedPath = receivedPath + fileName;
                        //Create new file
                        BinaryWriter writer = new BinaryWriter(File.Open(receivedPath, FileMode.Create));
                        writer.Close();
                        flag++;
                    }
                    if (flag >= 1)
                    {
                        BinaryWriter writer = new BinaryWriter(File.Open(receivedPath, FileMode.Append));
                        if (flag == 1)
                        {
                            writer.Write(state.buffer, 4 + fileNameLen, bytesRead - (4 + fileNameLen));
                            flag++;
                        }
                        else
                            writer.Write(state.buffer, 0, bytesRead);
                        writer.Close();
                        handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                        new AsyncCallback(ReadCallback), state);
                    }

                }
                else
                {
                    FileInfo fi = new FileInfo(receivedPath);
                    Console.WriteLine("File: {0} received. Saved at: {1}. {2} bytes received.\r\nPress [Enter] now", fi.Name, fi.Directory, fi.Length);
                    notDone = false;
                    allDone.Set();
                }

            }
        }


        /// <summary>
        /// Transfers without confirmation
        /// </summary>
        /// <param name="sendFilePath"></param>
        /// <param name="port"></param>
        public static void Client(string sendFilePath, int port)
        //Ref http://socketprogramming.blogspot.com.au/2007/11/file-transfer-using-c-socket.html
        {

            try
            {

                //IPAddress[] ipAddress = Dns.GetHostAddresses("localhost");
                //IPHostEntry hostEntry = Dns.GetHostEntry(Dns.GetHostName());

                //System.Net.IPAddress[] ipAddress = hostEntry.AddressList;

                //IPEndPoint ipEnd = new IPEndPoint(ipAddress[indxIPAddressToUse], port);
                //IPEndPoint ipEnd = new IPEndPoint(new IPAddress(new byte[] { 192, 168, 2, 106 }), Port);
                IPEndPoint ipEnd = new IPEndPoint(IPAddress.Parse(MySettings.Settings.SendToIPAddress), Port);


                Socket clientSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.IP);


                FileInfo fi = new FileInfo(sendFilePath);

                byte[] fileNameByte = Encoding.ASCII.GetBytes(fi.Name);




                byte[] fileData; // = File..ReadAllBytes(sendFilePath);

                if (File.Exists(sendFilePath))
                {

                    using (BinaryReader reader = new BinaryReader(File.OpenRead(sendFilePath)))
                    {
                        fileData = reader.ReadBytes((int)fi.Length);
                    }
                }
                else
                {
                    Console.WriteLine("File not found: {0}", sendFilePath);
                    return;
                }

                byte[] clientData = new byte[4 + fileNameByte.Length + fileData.Length];

                byte[] fileNameLen = BitConverter.GetBytes(fileNameByte.Length);



                fileNameLen.CopyTo(clientData, 0);

                fileNameByte.CopyTo(clientData, 4);

                fileData.CopyTo(clientData, 4 + fileNameByte.Length);



                clientSock.Connect(ipEnd);

                clientSock.Send(clientData);

                Console.WriteLine("File:{0} has been sent.{1} bytes sent.", sendFilePath, fi.Length);

                clientSock.Close();

            }

            catch (Exception ex)
            {

                Console.WriteLine("File Sending fail." + ex.Message);

            }
        }


    }
}







